//
//  LBCropView.m
//  LBCropImagePage
//
//  Created by mac on 2024/6/2.
//

#import "LBCropView.h"

typedef struct {
    CGFloat scale;
    CGAffineTransform transform;
    UIEdgeInsets contentInset;
} BMRotateFactor;

typedef struct {
    CGFloat lowerBound;
    CGFloat upperBound;
} BMCropWHRatioRange ;

typedef struct {
    BMRotateFactor rotateFactor;
    CGPoint contentScalePoint;
    CGFloat zoomScale;
    CGSize imageFrameSize;
} BMImageShowDiff;

@interface LBCropView () <UIScrollViewDelegate>

@property (nonatomic, strong) LBCropConfig *config;
@property (nonatomic, strong) UIImage *image;
@property (nonatomic, assign) CGFloat imageWHRatio;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) CAShapeLayer *shadeLayer;
@property (nonatomic, strong) CAShapeLayer *borderLayer;
@property (nonatomic, strong) CAShapeLayer *idleGridLayer;
@property (nonatomic, strong) CAShapeLayer *rotateGridLayer;

@property (nonatomic, assign) CGFloat actualAngle;
@property (nonatomic, assign) CGFloat angle;
@property (nonatomic, assign, readonly) CGFloat actualRadian;

///图片视图基于并适配`cropFrame` 的size
@property (nonatomic, assign) CGSize imageBoundsSize;

///裁剪宽高比
@property (nonatomic, assign) CGFloat cropWHRatio;

///裁剪框的frame
@property (nonatomic, assign) CGRect cropFrame;

///旋转基准角度 0°/360°、90°、180°、270°
@property (nonatomic, assign) kOriginAngle originAngle;

///可旋转的范围角度: -45° ~ 45°
@property (nonatomic, assign) CGFloat diffAngle;

///闲置时的网格数
@property (nonatomic, assign) BMGridCount idleGridCount;
///旋转时的网格数
@property (nonatomic, assign) BMGridCount rotateGridCount;
@property (nonatomic, assign) NSTimeInterval animDuration;

@end

@implementation LBCropView

- (void)dealloc
{
    NSLog(@"哈哈哈这里LBCropView释放了");
}

- (instancetype)initWithFrame:(CGRect)frame margin:(UIEdgeInsets)margin config:(LBCropConfig *)config
{
    return [self initWithFrame:frame
                        margin:margin
                        config:config
                 idleGridCount:BMGridCountMake(3, 3)
               rotateGridCount:BMGridCountMake(5, 5)];
}

- (instancetype)initWithFrame:(CGRect)frame 
                       margin:(UIEdgeInsets)margin
                       config:(LBCropConfig *)config
                idleGridCount:(BMGridCount)idleGridCount
              rotateGridCount:(BMGridCount)rotateGridCount
{
    if (self = [super initWithFrame:frame]) {
        self.config = config;
        [self setUpData];
        self.minMargin = self.margin = margin;
        [self setupUI];
        [self updateCropWHRatio:config.cropWHRatio 
                  idleGridCount:idleGridCount 
                rotateGridCount:rotateGridCount
                       animated:NO];
        [self rotate:config.angle];
        if (config.zoomScale != 0) {
            self.scrollView.zoomScale = config.zoomScale;
        }
        
        if (config.modified) {
            self.scrollView.contentOffset = config.contentOffset;
        }
    }
    return self;
}

- (void)setUpData
{
    self.image = self.config.image;
    self.imageWHRatio = self.image.size.width/self.image.size.height;
    self.diffAngle = 45;
    self.idleGridCount = (BMGridCount){0,0};
    self.rotateGridCount = (BMGridCount){0,0};
    self.animDuration = 0.3;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
    return self.scrollView;
}

#pragma mark - UIScrollViewDelegate

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

#pragma mark - public
///获取同步的configure, 可用于保存当前的裁剪状态， 下一次打开恢复状态
- (LBCropConfig *)synConfigure
{
    self.config.image = self.image;
    self.config.cropWHRatio = self.cropWHRatio;
    self.config.originAngle = self.originAngle;
    self.config.angle = self.angle;
    self.config.zoomScale = self.scrollView.zoomScale;
    self.config.contentOffset = self.scrollView.contentOffset;
    return self.config;
}

//旋转（单位：角度，基于`orginAngle` , 范围 `-45°` ～ `45°`）
- (void)rotate:(CGFloat)angle
{
    [self rotate:angle iaAutoZoom:false];
}

///向左旋转 （`originAngle - 90°`）
- (void)rotateLeft:(BOOL)animated
{
    CGFloat angle = self.angle;
    self.originAngle = [self prevOriginAngle];
    [self rotate:angle isAutoZoom:YES animated:animated];
}

///向右旋转（`originAngle` + 90°）
- (void)rotateRight:(BOOL)animated
{
    CGFloat angle = self.angle;
    self.originAngle = [self nextOriginAngle];
    [self rotate:angle isAutoZoom:YES animated:animated];
}

///刷新裁剪比例
- (void)updateCropWHRatio:(CGFloat)cropWHRatio
            idleGridCount:(BMGridCount)idleGridCount
          rotateGridCount:(BMGridCount)rotateGridCount
                 animated:(BOOL)animated
{
    // 1 算出改变后的UI数值，和改变前后的差值（获取scrollView最合适（不会超出）的cropFrame 和radian的 transform 和 contentInset）
    BMImageShowDiff diff = [self resetCropWHRatio:cropWHRatio];
    /* 2 立马设置 scrollView 改变后的transform, 和其他的一些差值， 让scrollView 形变后相对于之前的u
     状体啊看上去没有变化一样
    */
    [self tracelessUpdateTransform:diff.rotateFactor.transform
                 contentScalePoint:diff.contentScalePoint
                         zoomScale:diff.zoomScale
                    imageFrameSize:diff.imageFrameSize];
    /*
     3 再通过动画 刷新UI， 适配当前窗口，也就是把差值还原回去
     */
    [self updateUIWithRotateFactor:diff.rotateFactor
                 contentScalePoint:diff.contentScalePoint
                         zoomAcale:diff.zoomScale
                     idleGridCount:idleGridCount
                   rotateGridCount:rotateGridCount
                          animated:animated];
}


///显示旋转时的网格数
- (void)showRotateGrid:(BOOL)animated
{
    [self updateIdleGridAlpha:0 rotateGridAlpha:1 animated:animated];
}

//隐藏旋转时的网格数
- (void)hideRotateGrid:(BOOL)animated
{
    [self updateIdleGridAlpha:1 rotateGridAlpha:0 animated:animated];
}

//恢复：【调整角度=0】 + 【缩放比例 = 1】 + 【中心点】

- (void)recoverWithAnimated:(BOOL)animated
{
    self.angle = 0;
    BMRotateFactor factor = [self fitFactor];
    void (^updateScrollView)(void) = ^ {
        self.scrollView.zoomScale = 1;
        self.scrollView.transform = factor.transform;
        self.scrollView.contentInset = factor.contentInset;
        self.scrollView.contentOffset = [self fitOffset:CGPointMake(0.5, 0.5) 
                                            contentSize:CGSizeZero
                                           contentInset:factor.contentInset];
    };
    
    if (animated) {
        [UIView animateWithDuration:self.animDuration 
                              delay:0
                            options:UIViewAnimationOptionCurveEaseOut
                         animations:updateScrollView
                         completion:nil];
    } else {
        updateScrollView();
    }
}

/// 同步裁剪
/// - Parameter compressScale: 压缩比例， 默认为1，即原图尺寸

- (UIImage *)cropWithCompressAcale:(CGFloat)compressScale
{
    if (!self.image.CGImage) {
        return nil;
    }
    
    CGPoint fromPoint = CGPointMake(self.cropFrame.origin.x, CGRectGetMaxY(self.cropFrame));
    CGPoint convertTranslate = [self.borderLayer convertPoint:fromPoint toLayer:self.imageView.layer];
    
    CGFloat cropWHRati = self.cropWHRatio > 0 ? self.cropWHRatio : [self fitCropWHRation:self.imageWHRatio];
    CGFloat scale = [self scaleValue:self.scrollView.transform] * self.scrollView.zoomScale;
    
    CGFloat actualRadian = self.actualRadian;
    CGFloat imageViewHeght = CGRectGetHeight(self.imageView.bounds);
    
    CGImageRef imageRef = self.image.CGImage;
    
    UIImage *result = [[self class] cropWithCompressScale:compressScale
                                                 imageRef:imageRef
                                              cropWHRatio:cropWHRati
                                                    scale:scale
                                          covertTranslate:convertTranslate 
                                             actualRadian:actualRadian
                                          imageViewHeight:imageViewHeght];
    return result;
}


- (void)asyncCrop:(CGFloat)compressScale cropDone:(void (^)(UIImage *))cropDone
{
    CGImageRef imageRef = self.image.CGImage;
    if (!imageRef) {
        cropDone(nil);
        return;
    }
    
    CGFloat cropWHRatio = self.cropWHRatio > 0 ? self.cropWHRatio : [self fitCropWHRation:self.imageWHRatio];
    CGFloat scale = [self scaleValue:self.scrollView.transform] * self.scrollView.zoomScale;
    CGPoint convertTranslate = [self.borderLayer convertPoint:CGPointMake(self.cropFrame.origin.x, CGRectGetMaxY(self.cropFrame)) toLayer:self.imageView.layer];
    CGFloat radian = self.actualRadian;
    
    CGFloat height = self.imageView.bounds.size.height;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage *result = [[self class] cropWithCompressScale:compressScale 
                                                     imageRef:imageRef
                                                  cropWHRatio:cropWHRatio
                                                        scale:scale
                                              covertTranslate:convertTranslate
                                                 actualRadian:radian
                                              imageViewHeight:height];
        dispatch_async(dispatch_get_main_queue(), ^{
            cropDone(result);
        });
    });
}


#pragma mark - layout animation

- (void)setupUI
{
    self.clipsToBounds = YES;
    self.backgroundColor = [UIColor redColor];
    [self addSubview:self.scrollView];
    [self.scrollView addSubview:self.imageView];
    
    [self.layer addSublayer:self.shadeLayer];
    [self.layer addSublayer:self.idleGridLayer];
    [self.layer addSublayer:self.rotateGridLayer];
    [self.layer addSublayer:self.borderLayer];
}

- (UIBezierPath *)buildGridPathWithGridCount:(BMGridCount)gridCount
{
    UIBezierPath *gridPath = [[UIBezierPath alloc] init];
    if (gridCount.verCount > 1 && gridCount.horCount > 1) {
        CGFloat verSpace = CGRectGetHeight(self.cropFrame) / gridCount.verCount;
        CGFloat horSpace = CGRectGetWidth(self.cropFrame) / gridCount.horCount;
        for (NSInteger i = 1; i < gridCount.verCount; i ++) {
            CGFloat px = CGRectGetMinX(self.cropFrame);
            CGFloat py = CGRectGetMinY(self.cropFrame) + verSpace * i;
            [gridPath moveToPoint:CGPointMake(px, py)];
            [gridPath addLineToPoint:CGPointMake(CGRectGetMaxX(self.cropFrame), py)];
        }
        for (NSInteger i = 1; i < gridCount.horCount; i ++) {
            CGFloat px = CGRectGetMinX(self.cropFrame) + horSpace * i;
            CGFloat py = CGRectGetMinY(self.cropFrame);
            [gridPath moveToPoint:CGPointMake(px, py)];
            [gridPath addLineToPoint:CGPointMake(px, CGRectGetMaxY(self.cropFrame))];
        }
    }
    return gridPath;
}

- (void)buildAnimationOnLayer:(CALayer *)layer
                  withKeyPath:(NSString *)keyPath
                      toValue:(id)toValue
                     duration:(NSTimeInterval)duration
{
    [self buildAnimationOnLayer:layer
                    withKeypath:keyPath
                        toValue:toValue
                       duration:duration
             timingFunctionName:kCAMediaTimingFunctionEaseOut];
}

- (void)buildAnimationOnLayer:(CALayer *)layer
                  withKeypath:(NSString *)keyPath
                      toValue:(id)toValue
                     duration:(NSTimeInterval)duration
           timingFunctionName:(CAMediaTimingFunctionName)timingFunctionName
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:keyPath];
    animation.toValue = toValue;
    animation.fillMode = kCAFillModeBackwards;
    animation.duration = duration;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:timingFunctionName];
    [layer addAnimation:animation forKey:keyPath];
}

#pragma mark - 旋转实现

- (void)rotate:(CGFloat)angle 
    iaAutoZoom:(BOOL)isAutoZoom
{
    self.angle = angle;
    BMRotateFactor factor = [self fitFactor];
    
    CGFloat zoomScale = self.scrollView.zoomScale;
    
    if (!isAutoZoom) {
        CGFloat oldScale = [self scaleValue:self.scrollView.transform];
        CGFloat newScale = factor.scale;
        zoomScale *= oldScale/newScale;
    }
    
    CGFloat minZoomScale = self.scrollView.minimumZoomScale;
    if (zoomScale <= minZoomScale) {
        zoomScale = minZoomScale;
    }
    
    self.scrollView.transform = factor.transform;
    self.scrollView.contentInset = factor.contentInset;
    self.scrollView.zoomScale = zoomScale;
}

- (void)rotate:(CGFloat)angle
    isAutoZoom:(BOOL)isAutoZoom
      animated:(BOOL)animated
{
    if (!animated) {
        [self rotate:angle iaAutoZoom:isAutoZoom];
        return;
    }
    [UIView animateWithDuration:self.animDuration 
                          delay:0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
        [self rotate:angle iaAutoZoom:isAutoZoom];
    } completion:nil];
}

#pragma mark - Calculation

///裁剪宽高比的范围 （默认 最小 `1:2` ~ `2:1`）
- (BMCropWHRatioRange)cropWHRatioRange
{
    return (BMCropWHRatioRange){0.5, 2};
}

- (CGFloat)fitCropWHRation:(CGFloat)cropWHRatio
{
    CGFloat lowerBound = [self cropWHRatioRange].lowerBound;
    CGFloat upperBound = [self cropWHRatioRange].upperBound;
    
    if (cropWHRatio < lowerBound) {
        return cropWHRatio <= 0 ? 0 : lowerBound;
    }
    
    if (cropWHRatio > upperBound) {
        return upperBound;
    }
    
    return cropWHRatio;
}

//修正可裁剪的frame
- (CGRect)fitCropFrame
{
    UIEdgeInsets margin = self.margin;
    CGFloat maxW = CGRectGetWidth(self.bounds) - margin.left - margin.right;
    CGFloat maxH = CGRectGetHeight(self.bounds) - margin.top - margin.bottom;
    CGFloat whRation = self.cropWHRatio > 0 ? self.cropWHRatio : self.imageWHRatio;
    
    CGFloat w = maxW;
    CGFloat h = w/whRation;
    if (h > maxH) {
        h = maxH;
        w = h * whRation;
    }
    
    CGFloat x = (maxW - w) * 0.5 + margin.left;
    CGFloat y = (maxH - h) * 0.5 + margin.top;
    return CGRectMake(x, y, w, h);
}

- (CGSize)fitImageSize
{
    CGFloat imageW;
    CGFloat imageH;
    if ([self isLandscapeImage]) {
        imageH = self.cropFrame.size.height;
        imageW = imageH * self.imageWHRatio;
        if (imageW < self.cropFrame.size.width) {
            imageW = self.cropFrame.size.width;
            imageH = imageW / self.imageWHRatio;
        }
    } else {
        imageW = self.cropFrame.size.width;
        imageH = imageW/ self.imageWHRatio;
        if (imageH < self.cropFrame.size.height) {
            imageH = self.cropFrame.size.height;
            imageW = imageH * self.imageWHRatio;
        }
    }
    return CGSizeMake(imageW, imageH);
}

- (BMRotateFactor)fitFactor
{
    CGFloat imageW = self.imageBoundsSize.width;
    CGFloat imageH = self.imageBoundsSize.height;
    
    CGFloat cropW = self.cropFrame.size.width;
    CGFloat cropH = self.cropFrame.size.height;
    
    CGFloat actualRadian = self.actualRadian;
    
    double absRadian = fabs((double)actualRadian);
    CGFloat cosValue = (CGFloat)fabs(cos(absRadian));
    CGFloat sinValue = (CGFloat)fabs(sin(absRadian));
    
    CGFloat verSide1 = cosValue * cropH;
    CGFloat verSide2 = sinValue * cropW;
    CGFloat verSide = verSide1 + verSide2;
    
    CGFloat horSide1 = cosValue * cropW;
    CGFloat horSide2 = sinValue * cropH;
    CGFloat horSide = horSide1 + horSide2;
    
    CGFloat scale;
    
    if (imageW > cropW || imageH > cropH) {
        if (cropW > cropH) {
            CGFloat scale1 = verSide / imageH;
            CGFloat scale2 = horSide / cropW;
            scale = MAX(scale1, scale2);
        } else {
            CGFloat scale1 = horSide / imageW;
            CGFloat scale2 = verSide / cropH;
            scale = MAX(scale1, scale2);
        }
    } else {
        if ([self isLandscapeImage]) {
            scale = verSide / cropH;
        } else {
            scale = horSide / cropW;
        }
    }
    
    CGFloat verMargin = (cropH * scale - verSide) * 0.5 / scale;
    CGFloat horMargin = (cropW * scale - horSide) * 0.5 / scale;
    
    CGFloat top = verMargin + self.minMargin.top;
    CGFloat left = horMargin + self.minMargin.left;
    CGFloat bottom = verMargin + self.minMargin.bottom;
    CGFloat right = horMargin + self.minMargin.right;
    CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
    transform = CGAffineTransformRotate(transform, actualRadian);
    return (BMRotateFactor){scale, transform, UIEdgeInsetsMake(top, left, bottom, right)};
}

- (CGPoint)fitOffset:(CGPoint)contentScalePoint
         contentSize:(CGSize)contentSize
        contentInset:(UIEdgeInsets)contentInset
{
    CGRect sBounds = self.scrollView.bounds;
    CGSize size = contentSize;
    if (CGSizeEqualToSize(size, CGSizeZero)) {
        size = self.scrollView.contentSize;
    }
    
    CGFloat offsetX = contentScalePoint.x * size.width - sBounds.size.width * self.scrollView.layer.anchorPoint.x;
    CGFloat offsetY = contentScalePoint.y * size.height - sBounds.size.height * self.scrollView.layer.anchorPoint.y;
    
    if (UIEdgeInsetsEqualToEdgeInsets(contentInset, UIEdgeInsetsZero)) {
        return CGPointMake(offsetX, offsetY);
    }
    
    CGFloat maxOffsetX = size.width - sBounds.size.width + contentInset.right;
    CGFloat maxOffsetY = size.height - sBounds.size.height + contentInset.bottom;
    
    if (offsetX < - contentInset.left) {
        offsetX = -contentInset.left;
    } else if (offsetX > maxOffsetX) {
        offsetX = maxOffsetX;
    }
    
    if (offsetY < - contentInset.top) {
        offsetY = - contentInset.top;
    } else if (offsetY > maxOffsetY) {
        offsetY = maxOffsetY;
    }
    return CGPointMake(offsetX, offsetY);
}

#pragma mark - crop

+ (UIImage *)cropWithCompressScale:(CGFloat)compressScale
                          imageRef:(CGImageRef)imageRef
                       cropWHRatio:(CGFloat)cropWHRatio
                             scale:(CGFloat)scale
                   covertTranslate:(CGPoint)covertTranslate
                      actualRadian:(CGFloat)radian
                   imageViewHeight:(CGFloat)imageBoundsHeight
{
    CGFloat width = CGImageGetWidth(imageRef) * compressScale;
    CGFloat height = CGImageGetHeight(imageRef) * compressScale;
    
    //获取裁剪尺寸和裁剪区域
    CGSize rendSize;
    if (width > height) {
        rendSize = CGSizeMake(height * cropWHRatio, height);
        if (rendSize.width > width) {
            rendSize = CGSizeMake(width, width/ cropWHRatio);
        }
    } else {
        rendSize = CGSizeMake(width, width/ cropWHRatio);
        if (rendSize.height > height) {
            rendSize = CGSizeMake(height * cropWHRatio, height);
        }
    }
    
    CGBitmapInfo bitmapRawValue = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little;
    CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);;
    if (alphaInfo == kCGImageAlphaPremultipliedLast ||
        alphaInfo == kCGImageAlphaPremultipliedFirst ||
        alphaInfo == kCGImageAlphaLast ||
        alphaInfo == kCGImageAlphaLast) {
        bitmapRawValue |= kCGImageAlphaPremultipliedFirst;
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, rendSize.width, rendSize.height, 8, 0, colorSpace, bitmapRawValue);
    CGContextSetShouldAntialias(context, true);
    CGContextSetAllowsAntialiasing(context, true);
    CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
    
    CGFloat iScale = CGImageGetHeight(imageRef) / (imageBoundsHeight * scale);
    CGPoint translate = covertTranslate;
    translate.y = imageBoundsHeight - translate.y;
    translate.x *= - 1 * scale * iScale;
    translate.y *= - 1 * scale * iScale;
    CGContextConcatCTM(context, CGAffineTransformMakeScale(scale, scale));
    CGContextConcatCTM(context, CGAffineTransformMakeRotation(- radian));
    CGContextConcatCTM(context, CGAffineTransformMakeTranslation(translate.x, translate.y));
    
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
    
    CGImageRef newImageRef = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:newImageRef];
    
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(newImageRef);
    CGContextRelease(context);
    return result;
}

- (void)updateIdleGridAlpha:(CGFloat)idleGridAlpha
            rotateGridAlpha:(CGFloat)rotateGridAlpha
                   animated:(BOOL)animated
{
    if (animated) {
        [self buildAnimationOnLayer:self.idleGridLayer
                        withKeypath:@"opacity" 
                            toValue:@(0.12)
                           duration:self.animDuration
                 timingFunctionName:kCAMediaTimingFunctionEaseIn];
        [self buildAnimationOnLayer:self.rotateGridLayer
                        withKeypath:@"opacity"
                            toValue:@(0.12)
                           duration:self.animDuration
                 timingFunctionName:kCAMediaTimingFunctionEaseIn];
    }
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.idleGridLayer.opacity = idleGridAlpha;
    self.rotateGridLayer.opacity = rotateGridAlpha;
    [CATransaction commit];
}

- (void)updateUIWithRotateFactor:(BMRotateFactor)factor
               contentScalePoint:(CGPoint)contentScalePoint
                       zoomAcale:(CGFloat)zoomScale
                   idleGridCount:(BMGridCount)idleGridCount
                 rotateGridCount:(BMGridCount)rotateGridCount
                        animated:(BOOL)animated
{
    void (^updateScrollView)(void) = ^ {
        if (zoomScale) {
            self.scrollView.minimumZoomScale = 1;
            self.scrollView.zoomScale = 1;
        }
        self.scrollView.contentInset = factor.contentInset;
        self.scrollView.contentOffset = [self
                                         fitOffset:contentScalePoint
                                         contentSize:CGSizeZero
                                         contentInset:factor.contentInset];
    };
    //边框路径
    UIBezierPath *borderPath = [UIBezierPath bezierPathWithRect:self.cropFrame];
    
    //阴影路径
    UIBezierPath *shadePath = [UIBezierPath bezierPathWithRect:self.bounds];
    [shadePath appendPath:borderPath];
    //闲置网格路径
    self.idleGridCount = idleGridCount;
    
    UIBezierPath *idleGridPath = [self buildGridPathWithGridCount:self.idleGridCount];
    //旋转网格路径
    self.rotateGridCount = rotateGridCount;
    
    UIBezierPath *rotateGridPath = [self buildGridPathWithGridCount:self.rotateGridCount];
    
    if (animated) {
        [UIView animateWithDuration:self.animDuration
                              delay:0
                            options:UIViewAnimationOptionCurveEaseOut
                         animations:updateScrollView
                         completion:nil];
        [self buildAnimationOnLayer:self.borderLayer
                        withKeyPath:@"path"
                            toValue:(__bridge id)borderPath.CGPath
                           duration:self.animDuration];
        [self buildAnimationOnLayer:self.shadeLayer 
                        withKeyPath:@"path"
                            toValue:(__bridge  id)shadePath.CGPath
                           duration:self.animDuration];
        [self buildAnimationOnLayer:self.idleGridLayer
                        withKeyPath:@"path"
                            toValue:(__bridge id)idleGridPath.CGPath
                           duration:self.animDuration];
        [self buildAnimationOnLayer:self.rotateGridLayer 
                        withKeyPath:@"path"
                            toValue:(__bridge id)rotateGridPath.CGPath
                           duration:self.animDuration];
    } else {
        updateScrollView();
    }
    [CATransaction begin];
    [CATransaction setDisableActions:YES];
    self.borderLayer.path = borderPath.CGPath;
    self.shadeLayer.path = shadePath.CGPath;
    self.idleGridLayer.path = idleGridPath.CGPath;
    self.rotateGridLayer.path = rotateGridPath.CGPath;
    [CATransaction commit];
}

- (BMImageShowDiff)resetCropWHRatio:(CGFloat)whRatio
{
    self.cropWHRatio = whRatio;
    
    ///更新可裁剪区域
    CGRect oldCropFrame = self.cropFrame;
    self.cropFrame = [self fitCropFrame];
    
    //更新imageView基于 并适配 cropFrame 后的原始 size
    self.imageBoundsSize = [self fitImageSize];
    
    //更新基于并适配 cropFrame 后的最小边距
    self.minMargin = UIEdgeInsetsMake(self.cropFrame.origin.y, self.cropFrame.origin.x, CGRectGetHeight(self.bounds) - (self.cropFrame.origin.y + self.cropFrame.size.height), CGRectGetWidth(self.bounds) - (self.cropFrame.origin.x + self.cropFrame.size.width));
    
    //获取scrollView 最合适（不会超出） 的 cropFrame 和 radian 的 transform 和 contentInset
    BMRotateFactor factor = [self fitFactor];
    
    //当前裁剪框中心点相对于当前内容图片的百分比位置
    CGPoint contentScalePoint = CGPointMake(0.5, 0.5);
    CGFloat zoomScale = 1;
    if (CGRectGetWidth(self.imageView.bounds) > 0) {
        CGPoint fromPoint = CGPointMake(oldCropFrame.origin.x + oldCropFrame.size.width/2, oldCropFrame.origin.y + oldCropFrame.size.height/2);
        CGPoint convertOffset = [self.borderLayer convertPoint:fromPoint toLayer:self.imageView.layer];
        contentScalePoint.x = convertOffset.x / self.imageView.bounds.size.width;
        contentScalePoint.y = convertOffset.y / self.imageView.bounds.size.height;
        CGFloat diffScale = [self scaleValue:self.scrollView.transform] / [self scaleValue:factor.transform];
        zoomScale = CGRectGetWidth(self.imageView.bounds) / self.imageBoundsSize.width * diffScale;
    }
    
    CGSize imageFrameSize = CGSizeMake(self.imageBoundsSize.width * zoomScale, self.imageBoundsSize.height * zoomScale);
    return (BMImageShowDiff){factor,contentScalePoint, zoomScale, imageFrameSize};
}

- (void)tracelessUpdateTransform:(CGAffineTransform)tranform
               contentScalePoint:(CGPoint)contentScalePoint
                       zoomScale:(CGFloat)zoomScale
                  imageFrameSize:(CGSize)imageFrameSize
{
    /*return*/;
    CGFloat ax = (self.cropFrame.origin.x + self.cropFrame.size.width/2) / CGRectGetWidth(self.bounds);
    CGFloat ay = (self.cropFrame.origin.y + self.cropFrame.size.height/2) / CGRectGetHeight(self.bounds);
    self.scrollView.layer.anchorPoint = CGPointMake(ax, ay);
    self.scrollView.layer.position = CGPointMake(ax * CGRectGetWidth(self.bounds), ay * CGRectGetHeight(self.bounds));
    
    self.imageView.bounds = CGRectMake(0, 0, self.imageBoundsSize.width, self.imageBoundsSize.height);
    self.scrollView.transform = tranform;
    if (zoomScale < 1) {
        self.scrollView.minimumZoomScale = zoomScale;
    }
    self.scrollView.zoomScale = zoomScale;
    self.scrollView.contentSize = imageFrameSize;
    self.imageView.frame = CGRectMake(0, 0, imageFrameSize.width, imageFrameSize.height);
    self.imageView.backgroundColor = [UIColor cyanColor];
    self.scrollView.contentOffset = [self fitOffset:contentScalePoint
                                        contentSize:imageFrameSize
                                       contentInset:UIEdgeInsetsZero];
}

#pragma mark - lazy load

- (UIScrollView *)scrollView
{
    if (!_scrollView) {
        _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
        _scrollView.delegate = self;
        _scrollView.clipsToBounds = false;
        _scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        _scrollView.minimumZoomScale = 1;
        _scrollView.maximumZoomScale = 10;
        _scrollView.bounces = YES;
        _scrollView.alwaysBounceVertical = YES;
        _scrollView.alwaysBounceHorizontal = YES;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.backgroundColor = [UIColor cyanColor];
    }
    return _scrollView;
}

- (UIImageView *)imageView
{
    if (!_imageView) {
        _imageView = [[UIImageView alloc] initWithImage:self.image];
        _imageView.frame = CGRectZero;
    }
    return _imageView;
}

- (CAShapeLayer *)shadeLayer
{
    if (!_shadeLayer) {
        _shadeLayer = [[CAShapeLayer alloc] init];
        _shadeLayer.frame = self.bounds;
        _shadeLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.5].CGColor;
        _shadeLayer.fillRule = kCAFillRuleEvenOdd;
        _shadeLayer.strokeColor = [UIColor clearColor].CGColor;
        _shadeLayer.lineWidth = 0.35;
    }
    return _shadeLayer;
}

- (CAShapeLayer *)idleGridLayer
{
    if (!_idleGridLayer) {
        _idleGridLayer = [[CAShapeLayer alloc] init];
        _idleGridLayer.frame = self.bounds;
        _idleGridLayer.fillColor = [UIColor clearColor].CGColor;
        _idleGridLayer.strokeColor = [UIColor colorWithWhite:1 alpha:0.8].CGColor;
        _idleGridLayer.lineWidth = 0.35;
    }
    return _idleGridLayer;
}

- (CAShapeLayer *)rotateGridLayer
{
    if (!_rotateGridLayer) {
        _rotateGridLayer = [[CAShapeLayer alloc] init];
        _rotateGridLayer.frame = self.bounds;
        _rotateGridLayer.fillColor = [UIColor clearColor].CGColor;
        _rotateGridLayer.strokeColor = [UIColor colorWithWhite:1 alpha:0.8].CGColor;
        _rotateGridLayer.lineWidth = 0.35;
        _rotateGridLayer.opacity = 0;
    }
    return _rotateGridLayer;
}

- (CAShapeLayer *)borderLayer
{
    if (!_borderLayer) {
        _borderLayer = [[CAShapeLayer alloc] init];
        _borderLayer.frame = self.bounds;
        _borderLayer.fillColor = [UIColor clearColor].CGColor;
        _borderLayer.strokeColor = [UIColor whiteColor].CGColor;
        _borderLayer.lineWidth = 1;
    }
    return _borderLayer;
}

#pragma mark - util

- (UIBezierPath *)buildGridPathWithGridCount:(CGSize)gridCount cropFrame:(CGRect)cropFrame
{
    UIBezierPath *gridPath = [UIBezierPath new];
    if (gridCount.width <= 1 ||
        gridCount.height <= 1) {
        return gridPath;
    }
    CGFloat verSpace = cropFrame.size.height/gridCount.height;
    CGFloat horSpace = cropFrame.size.width/gridCount.width;
    for (NSInteger i = 1; i < gridCount.height; i ++) {
        CGFloat px = cropFrame.origin.x;
        CGFloat py = cropFrame.origin.y + verSpace * i;
        [gridPath moveToPoint:CGPointMake(px, py)];
        [gridPath addLineToPoint:CGPointMake(px + cropFrame.size.width, py)];
    }
    for (NSInteger i = 1; i < gridCount.width; i ++) {
        CGFloat px = cropFrame.origin.x + horSpace * i;
        CGFloat py = cropFrame.origin.y;
        [gridPath moveToPoint:CGPointMake(px, py)];
        [gridPath addLineToPoint:CGPointMake(px, py + cropFrame.size.height)];
    }
    return gridPath;
}

- (kOriginAngle)prevOriginAngle
{
    switch (self.originAngle) {
        case OriginAngleDeg0:
            return OriginAngleDeg270;
            break;
        case OriginAngleDeg90:
            return OriginAngleDeg0;
            break;;
        case OriginAngleDeg180:
            return OriginAngleDeg90;
            break;
        case OriginAngleDeg270:
            return OriginAngleDeg180;
            break;
        default:
            break;
    }
}

- (kOriginAngle)nextOriginAngle
{
    switch (self.originAngle) {
        case OriginAngleDeg0:
            return OriginAngleDeg90;
            break;
        case OriginAngleDeg90:
            return OriginAngleDeg180;
            break;
        case OriginAngleDeg180:
            return OriginAngleDeg270;
            break;
        case OriginAngleDeg270:
            return OriginAngleDeg0;
            break;
        default:
            break;
    }
}

- (CGFloat)scaleValue:(CGAffineTransform)t
{
    return sqrt(t.a * t.a + t.c * t.c);
}

//是否横屏的图片
- (BOOL)isLandscapeImage
{
    return self.imageWHRatio > 1;
}

///当前实际的旋转弧度 （范围 `0` ~ `2π`）
- (CGFloat)actualRadian
{
    return (self.actualAngle/180.0) * M_PI;
}

///当前调整的旋转弧度（基于 `originAngle`, 范围 `-π/4` ~ 'π/4'）
- (CGFloat)radian
{
    return (self.angle/180.0) * M_PI;
}

//当前调整的旋转角度 （基于 `originAngle`, 范围 `-45°` ~ `45°`）

- (CGFloat)angle
{
    return self.actualAngle - self.originAngle;
}

- (void)setAngle:(CGFloat)angle
{
    if ((angle + self.originAngle) > [self angleRangeMax]) {
        self.actualAngle = [self angleRangeMax];
    } else if ((self.originAngle - angle) < [self angleRangeMin]) {
        self.actualAngle = [self angleRangeMin];
    } else {
        self.actualAngle = self.originAngle + angle;
    }
}

- (CGFloat)angleRangeMax
{
    return self.originAngle + self.diffAngle;
}

- (CGFloat)angleRangeMin
{
    return self.originAngle - self.diffAngle;
}

@end
